package di

import (
	"fmt"
	"gitee.com/ltotal/ppw_gin/pkg"
	"gorm.io/gorm"
)

type (
	Config     = *pkg.ConfigPkg
	Apollo     = *pkg.ApolloPkg
	Tracer     = *pkg.TracerPkg
	Database   = *pkg.DatabasePkg
	Redis      = *pkg.Redis
	Logger     = *pkg.LoggerPkg
	HTTP       = func() *pkg.HTTPPkg
	Session    = pkg.Session
	Routes     = pkg.Routes
	Rule       = pkg.Rule
	Validators = pkg.Validators
)

// ConfigConf 基础本地初始化配置
type ConfigConf struct {
	ConfigPath       string               `mapstructure:"config_path"`
	ConfigType       string               `mapstructure:"config_type"`
	ApolloConf       string               `mapstructure:"apollo_conf"`
	ApUpdateCallback pkg.ApUpdateCallback `mapstructure:"ap_update_callback"`
	InitCallback     func()               `mapstructure:"init_callback"`
}

// TracerConf 链路跟踪初始化配置
type TracerConf struct {
	Enable          bool   `mapstructure:"enable"`
	Host            string `mapstructure:"host"`
	Port            int    `mapstructure:"port"`
	MainServiceName string `mapstructure:"main_service_name"`
}

// DatabaseConf 数据库初始化配置
type DatabaseConf struct {
	DBKey    string         `mapstructure:"db_key"`
	CallBack func(*gorm.DB) `mapstructure:"call_back"`
}

// RedisConf redis缓存初始化配置
type RedisConf struct {
	Host      string `mapstructure:"host"`
	Port      int    `mapstructure:"port"`
	Password  string `mapstructure:"password"`
	PoolSize  int    `mapstructure:"pool_size"`
	DefaultDB int    `mapstructure:"default_db"`
	Prefix    string `mapstructure:"prefix"`
}

// LoggerConf 日志初始化配置
type LoggerConf struct {
	LogDir     string         `mapstructure:"log_dir"`      // 日志目录
	PreLogFunc pkg.PreLogFunc `mapstructure:"pre_log_func"` // 日志记录前的前置处理方法
	Log2Tracer bool           `mapstructure:"log_2_tracer"` // 是否同时把日志记录到链路跟踪
}

// SessionConf LoggerConf 日志初始化配置
type SessionConf struct {
	Prefix       string `mapstructure:"prefix"`
	CookieName   string `mapstructure:"cookie_name"`
	CookieDomain string `mapstructure:"cookie_domain"`
	Secure       bool   `mapstructure:"secure"`
	HTTPOnly     bool   `mapstructure:"http_only"`
}

func Initialize() {
	// 依赖组件注入
	// MustInvoke表示之前必须已经注册该组件的依赖配置(否则panic)，并根据该配置注入实际组件
	// Invoke表示之前如果注册了该组件的依赖配置，则根据该配置注入实际组件，否则不注入
	ProvideConfig()
	ProvideTracer()
	ProvideDatabase()
	ProvideRedis()
	ProvideLogger()
	ProvideHTTP()
	ProvideSession()
}

func ProvideConfig() {
	MustInvoke(func(conf *ConfigConf) {
		pkg.Config.Init(conf.ConfigPath, conf.ConfigType)
		if conf.ApolloConf != "" {
			apConf := &pkg.ApolloConfPkg{}
			if err := pkg.Config.Read(conf.ApolloConf).Unmarshal(apConf); err != nil {
				panic(err)
			}
			if err := pkg.Apollo.Start(apConf, conf.ApUpdateCallback); err != nil {
				panic(err)
			}
		}
		if conf.InitCallback != nil {
			conf.InitCallback()
		}
		if err := container.Provide(func() *pkg.ConfigPkg {
			return pkg.Config
		}); err != nil {
			panic(err)
		}
		if err := container.Provide(func() *pkg.ApolloPkg {
			return pkg.Apollo
		}); err != nil {
			panic(err)
		}
	})
}

func ProvideTracer() {
	_ = Invoke(func(conf *TracerConf) {
		if conf.Enable {
			serviceAddr := fmt.Sprintf("%s:%d", conf.Host, conf.Port)
			serviceName := conf.MainServiceName
			pkg.Tracer.Init(serviceAddr, serviceName, true)
		}
		if err := container.Provide(func() *pkg.TracerPkg {
			return pkg.Tracer
		}); err != nil {
			panic(err)
		}
	})
}

func ProvideDatabase() {
	_ = Invoke(func(conf []*DatabaseConf) {
		for _, item := range conf {
			cf := &pkg.DBPoolConfig{}
			if err := pkg.Apollo.Struct(item.DBKey, cf); err != nil {
				panic(err)
			}
			pkg.Database.Init(cf, item.CallBack)
		}
		if err := container.Provide(func() *pkg.DatabasePkg {
			return pkg.Database
		}); err != nil {
			panic(err)
		}
	})
}

func ProvideRedis() {
	_ = Invoke(func(conf *RedisConf) {
		pkg.RedisCli.Init(
			conf.Host,
			conf.Port,
			conf.Password,
			conf.PoolSize,
			conf.DefaultDB,
			conf.Prefix,
		)
		if err := container.Provide(func() *pkg.Redis {
			return pkg.RedisCli
		}); err != nil {
			panic(err)
		}
	})
}

func ProvideLogger() {
	_ = Invoke(func(conf *LoggerConf) {
		pkg.Logger = pkg.NewLogger(conf.LogDir, conf.PreLogFunc, conf.Log2Tracer)
		if err := container.Provide(func() *pkg.LoggerPkg {
			return pkg.Logger
		}); err != nil {
			panic(err)
		}
	})
}

func ProvideSession() {
	_ = Invoke(func(conf *SessionConf, redis Redis) {
		if err := container.Provide(func() Session {
			return pkg.NewSession(redis, conf.Prefix, conf.CookieName, conf.CookieDomain, conf.Secure, conf.HTTPOnly)
		}); err != nil {
			panic(err)
		}
	})
}

func ProvideHTTP() {
	// http组件无需依赖配置，直接注入组件
	MustProvide(func() func() *pkg.HTTPPkg {
		return func() *pkg.HTTPPkg {
			return pkg.NewHTTP()
		}
	})
}
